 /*******************************************************************************
  * Copyright (c) 2004 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Common Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/cpl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.progress;
 import java.util.ArrayList ;
 import java.util.Collection ;

 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.dialogs.Dialog;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.viewers.IContentProvider;
 import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.IStructuredContentProvider;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITableLabelProvider;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.jface.viewers.ViewerSorter;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.dnd.Clipboard;
 import org.eclipse.swt.dnd.TextTransfer;
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.List;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.MenuItem;
 import org.eclipse.swt.widgets.Shell;
 /**
  * The ErrorNotificationDialog is is the dialog that comes up when an error has
  * occured.
  */
 public class ErrorNotificationDialog extends Dialog {
     TableViewer errorViewer;
     Button clearButton;
     List detailsList;
     private Clipboard clipboard;
     private ErrorInfo selectedError = null;

     /**
      * Reserve room for this many details list items.
      */
     private static final int DETAILS_LIST_ITEM_COUNT = 7;

     /**
      * The nesting indent.
      */
     private static final String NESTING_INDENT = " "; //$NON-NLS-1$

     /**
      * Create a new instance of the receiver.
      *
      * @param parentShell
      */
     public ErrorNotificationDialog(Shell parentShell) {
         super(parentShell == null
                 ? ProgressManagerUtil.getDefaultParent()
                 : parentShell);
         setBlockOnOpen(false);
         setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE
                 | SWT.RESIZE);
     }
     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
      */
     protected void configureShell(Shell newShell) {
         super.configureShell(newShell);
         newShell.setText(ProgressMessages
                 .getString("ErrorNotificationDialog.ErrorNotificationTitle")); //$NON-NLS-1$
 newShell.addDisposeListener(new DisposeListener() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
              */
             public void widgetDisposed(DisposeEvent e) {
                 getManager().clearDialog();
             }
         });
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.window.Window#getShellStyle()
      */
     protected int getShellStyle() {
         return super.getShellStyle() | SWT.MIN;
     }
     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
      */
     protected Control createDialogArea(Composite parent) {
         initializeDialogUnits(parent);
         Composite topArea = (Composite) super.createDialogArea(parent);
         errorViewer = new TableViewer(topArea, SWT.MULTI | SWT.H_SCROLL
                 | SWT.V_SCROLL | SWT.BORDER);
         errorViewer.setSorter(getViewerSorter());
         errorViewer.getControl().addMouseListener(new MouseAdapter() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.MouseAdapter#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
              */
             public void mouseDoubleClick(MouseEvent e) {
                 openErrorDialog();
             }
         });
         errorViewer
                 .addSelectionChangedListener(new ISelectionChangedListener() {
                     public void selectionChanged(SelectionChangedEvent event) {

                         clearButton.setEnabled(!errorViewer.getSelection()
                                 .isEmpty());
                         setDetailsContents();
                     }
                 });
         Control control = errorViewer.getControl();
         GridData data = new GridData(GridData.FILL_BOTH
                 | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
         data.widthHint = convertWidthInCharsToPixels(60);
         data.heightHint = convertHeightInCharsToPixels(10);
         control.setLayoutData(data);
         initContentProvider();
         initLabelProvider();
         applyDialogFont(parent);

         createDetailsList(topArea);
         return topArea;
     }
     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
      */
     protected void createButtonsForButtonBar(Composite parent) {

         clearButton = createButton(
                 parent,
                 IDialogConstants.CLIENT_ID + 2,
                 ProgressMessages
                         .getString("ErrorNotificationDialog.ClearButtonTitle"), false); //$NON-NLS-1$
 clearButton.setEnabled(false);
         clearButton.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent e) {
                 ISelection rawSelection = errorViewer.getSelection();
                 if (rawSelection != null
                         && rawSelection instanceof IStructuredSelection) {
                     IStructuredSelection selection = (IStructuredSelection) rawSelection;
                     getManager().removeErrors(selection.toList());
                 }
                 refresh();
             }
         });
         Button button = createButton(parent, IDialogConstants.CLOSE_ID,
                 IDialogConstants.CLOSE_LABEL, true);
         button.addSelectionListener(new SelectionListener() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
              */
             public void widgetSelected(SelectionEvent e) {
                 close();
             }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
              */
             public void widgetDefaultSelected(SelectionEvent e) {
                 close();
             }
         });
     }
     /**
      * Return a viewer sorter for looking at the jobs.
      *
      * @return
      */
     private ViewerSorter getViewerSorter() {
         return new ViewerSorter() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.ViewerSorter#compare(org.eclipse.jface.viewers.Viewer,
              * java.lang.Object, java.lang.Object)
              */
             public int compare(Viewer testViewer, Object e1, Object e2) {
                 return ((Comparable ) e1).compareTo(e2);
             }
         };
     }
     /**
      * Sets the content provider for the viewer.
      */
     protected void initContentProvider() {
         IContentProvider provider = new IStructuredContentProvider() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IContentProvider#dispose()
              */
             public void dispose() {
                 //Nothing of interest here
 }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
              */
             public Object [] getElements(Object inputElement) {
                 return getManager().getErrors().toArray();
             }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
              * java.lang.Object, java.lang.Object)
              */
             public void inputChanged(Viewer viewer, Object oldInput,
                     Object newInput) {
                 if (newInput != null)
                     refresh();
             }
         };
         errorViewer.setContentProvider(provider);
         errorViewer.setInput(getManager());
     }
     /**
      * Get the notificationManager that this is being created for.
      *
      * @return
      */
     private ErrorNotificationManager getManager() {
         return ProgressManager.getInstance().errorManager;
     }
     /**
      * Refresh the contents of the viewer.
      */
     void refresh() {
         errorViewer.refresh();
     }
     private void initLabelProvider() {
         ITableLabelProvider provider = new ITableLabelProvider() {
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
              */
             public void addListener(ILabelProviderListener listener) {
                 //Do nothing
 }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
              */
             public void dispose() {
                 //Do nothing
 }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object,
              * int)
              */
             public Image getColumnImage(Object element, int columnIndex) {
                 return JFaceResources.getImageRegistry().get(
                         ErrorNotificationManager.ERROR_JOB_KEY);
             }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object,
              * int)
              */
             public String getColumnText(Object element, int columnIndex) {
                 return ((ErrorInfo) element).getDisplayString();
             }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
              * java.lang.String)
              */
             public boolean isLabelProperty(Object element, String property) {
                 return false;
             }
             /*
              * (non-Javadoc)
              *
              * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
              */
             public void removeListener(ILabelProviderListener listener) {
                 //Do nothing
 }
         };
         errorViewer.setLabelProvider(provider);
     }
     /**
      * Open the error dialog on the current selection.
      */
     private void openErrorDialog() {
         ErrorInfo element = getSingleSelection();
         if (element == null)
             return;
         ErrorDialog.openError(getShell(), element.getDisplayString(), null,
                 element.getErrorStatus());
     }
     /**
      * Get the single selection. Return null if the selection is not just one
      * element.
      *
      * @return ErrorInfo or <code>null</code>.
      */
     private ErrorInfo getSingleSelection() {
         ISelection rawSelection = errorViewer.getSelection();
         if (rawSelection != null
                 && rawSelection instanceof IStructuredSelection) {
             IStructuredSelection selection = (IStructuredSelection) rawSelection;
             if (selection.size() == 1)
                 return (ErrorInfo) selection.getFirstElement();
         }
         return null;
     }
     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.Dialog#close()
      */
     public boolean close() {
         getManager().clearAllErrors();
         Rectangle shellPosition = getShell().getBounds();
         boolean result = super.close();
         ProgressManagerUtil.animateDown(shellPosition);
         return result;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
      */
     protected void initializeBounds() {
         super.initializeBounds();
         Rectangle shellPosition = getShell().getBounds();
         ProgressManagerUtil.animateUp(shellPosition);
     }

     /**
      * Create this dialog's drop-down list component.
      *
      * @param detailsParent
      * the parent composite
      */
     private void createDetailsList(Composite detailsParent) {
         // create the list
 detailsList = new List (detailsParent, SWT.BORDER | SWT.H_SCROLL
                 | SWT.V_SCROLL | SWT.MULTI);

         GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
                 | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL
                 | GridData.GRAB_VERTICAL);
         data.heightHint = detailsList.getItemHeight() * DETAILS_LIST_ITEM_COUNT;
         data.horizontalSpan = 2;
         detailsList.setLayoutData(data);
         Menu copyMenu = new Menu(detailsList);
         MenuItem copyItem = new MenuItem(copyMenu, SWT.NONE);
         copyItem.addSelectionListener(new SelectionListener() {
             /*
              * @see SelectionListener.widgetSelected (SelectionEvent)
              */
             public void widgetSelected(SelectionEvent e) {
                 copyToClipboard();
             }
             /*
              * @see SelectionListener.widgetDefaultSelected(SelectionEvent)
              */
             public void widgetDefaultSelected(SelectionEvent e) {
                 copyToClipboard();
             }
         });
         copyItem.setText(JFaceResources.getString("copy")); //$NON-NLS-1$
 detailsList.setMenu(copyMenu);
     }

     /**
      * Set the contents of the details list to be the status from the selected
      * error.
      */
     private void setDetailsContents() {

         Collection statusList = new ArrayList ();

         ErrorInfo info = getSingleSelection();

         if (info != null) {
             selectedError = info;
             statusList.add(selectedError.getErrorStatus().getMessage());
             if (selectedError.getErrorStatus().getException() != null) {
                 Throwable exception = selectedError.getErrorStatus()
                         .getException();
                 statusList.add(exception.toString());
                 StackTraceElement [] elements = exception.getStackTrace();
                 for (int i = 0; i < elements.length; i++) {
                     statusList.add(elements[i].toString());
                 }
             }
             IStatus[] statuses = (selectedError.getErrorStatus().getChildren());
             for (int i = 0; i < statuses.length; i++) {
                 statusList.add(NESTING_INDENT + statuses[i].getMessage());
             }
         }

         String [] items = new String [statusList.size()];
         statusList.toArray(items);

         detailsList.setItems(items);

     }

     /**
      * Copy the contents of the statuses to the clipboard.
      */
     private void copyToClipboard() {

         if (selectedError == null)
             return;

         if (clipboard != null)
             clipboard.dispose();

         StringBuffer statusBuffer = new StringBuffer ();
         populateCopyBuffer(selectedError.getErrorStatus(), statusBuffer, 0);
         clipboard = new Clipboard(detailsList.getDisplay());
         clipboard.setContents(new Object []{statusBuffer.toString()},
                 new Transfer[]{TextTransfer.getInstance()});
     }

     /**
      * Put the details of the status of the error onto the stream.
      *
      * @param buildingStatus
      * @param buffer
      * @param nesting
      */
     private void populateCopyBuffer(IStatus buildingStatus,
             StringBuffer buffer, int nesting) {

         for (int i = 0; i < nesting; i++) {
             buffer.append(NESTING_INDENT); //$NON-NLS-1$
 }
         buffer.append(buildingStatus.getMessage());

         if (buildingStatus.getException() != null) {
             Throwable exception = buildingStatus.getException();
             buffer.append("\n"); //$NON-NLS-1$
 buffer.append(exception.toString());
             StackTraceElement [] elements = exception.getStackTrace();
             for (int i = 0; i < elements.length; i++) {
                 buffer.append("\n"); //$NON-NLS-1$
 buffer.append(elements[i].toString());

             }
         }

         buffer.append("\n"); //$NON-NLS-1$
 IStatus[] children = buildingStatus.getChildren();
         for (int i = 0; i < children.length; i++) {
             populateCopyBuffer(children[i], buffer, nesting + 1);
         }
     }
 }
